---
name: 设计理念
route: /design
order: 3
sidebar: false
---


## 原理分析
`vue-next`是Vue3的源码仓库，Vue3采用lerna做package的划分，而响应式能力`@vue/reactivity`被划分到了单独的一个package中

从这个包提供的几个核心api来分析：

### effect
effect其实是响应式库中一个通用的概念：`观察函数`，就像Vue2中的`Watcher`，mobx中的`autorun`，`observer`一样，它的作用是`收集依赖`。

它接受的是一个函数，这个函数内部对于响应式数据的访问都可以收集依赖，那么在响应式数据更新后，就会触发响应的更新事件。

### reactive
响应式数据的核心api，这个api返回的是一个`proxy`，对上面所有属性的访问都会被劫持，从而在get的时候收集依赖（也就是正在运行的`effect`），在set的时候触发更新。

### ref
对于简单数据类型比如`number`，我们不可能像这样去做：
```js
let data = reactive(2)
// 😭oops
data = 5
```
这是不符合响应式的拦截规则的，没有办法能拦截到`data`本身的改变，只能拦截到`data`身上的属性的改变，所以有了ref。
```js
const data = ref(2)
// 💕ok
data.value= 5
```

### computed
计算属性，依赖值更新以后，它的值也会随之自动更新。其实computed内部也是一个effect。

拥有在computed中观察另一个computed数据、effect观察computed改变之类的高级特性。

## 实现
从这几个核心api来看，只要effect能接入到React系统中，那么其他的api都没什么问题，因为它们只是去收集effect的依赖，去通知effect触发更新。

effect接受的是一个函数，而且effect还支持通过传入`schedule`参数来自定义依赖更新的时候需要触发什么函数，

而`react-reactivity`的核心api: `useStore`接受的也是一个函数`selector`，它会让用户自己选择在组件中需要访问的数据。

那么思路就显而易见了：

1. 把`selector`包装在effect中执行，去收集依赖。
2. 指定依赖发生更新时，需要调用的函数是`当前正在使用useStore`的这个组件的`forceUpdate`强制渲染函数。

这样不就实现了数据变化，组件自动更新吗？

简单的看一下核心实现

```js
export const useStore = (selector) => {
  const store = useContext(storeContext)
  if (!store) throw new Error('没有发现context的value,请在组件上使用<Provider>包裹！')
  const [, forceUpdateDispatch] = useReducer(v => v + 1, 0)
  let runner
  if (!runner) {
    runner = effect(
      () => selector(store),
      {
        lazy: true,
        scheduler: j => {
          if (j() === undefined) return
          forceUpdateDispatch()
        }
      })
  }
  useEffect(() => () => stop(runner), []) //卸载组件后取消effect
  const value = runner()
  //自动脱 Ref 功能>>判断对象是ref 则返回.value (在渲染环境不需要 .value 去取值，极大的减少的心智负担。)
  for (let key in value) if (isRef(value[key])) value[key] = value[key].value
  return value
}
```

1. 先通过useReducer在当前组件中注册一个强制更新的函数。
2. 通过useContext读取用户从Provider中传入的store。
3. 再通过Vue的effect去帮我们执行selector(store)，并且指定scheduler为forceUpdate，这样就完成了依赖收集。

就简单的几行代码，就实现了在React中使用`@vue/reactivity`中的所有能力。

## 优点：
1. 直接引入@vue/reacivity，完全使用Vue3的reactivity能力，拥有computed, effect等各种能力，并且对于Set和Map也提供了响应式的能力。后续也会随着这个库的更新变得更加完善的和强大。
2. vue-next仓库内部完整的测试用例。
3. 完善的TypeScript类型支持。
4. 完全复用@vue/reacivity实现超强的全局状态管理能力。
5. 状态管理中组件级别的精确更新。
6. Vue3总是要学的嘛，提前学习防止失业！

## 缺点：
1. 由于需要精确的收集依赖全靠`useStore`，所以`selector`函数一定要精确的访问到你关心的数据。甚至如果你需要触发数组内部某个值的更新，那你在useStore中就不能只返回这个数组本身。

举一个例子：
```js
function ViewArea() {
  const msgList = useStore((store) => {
    return store.state.msgList.map((msg, i) => (
      <div onClick={() => mutations.delMsg2(i)} key={i}>
        {msg}
      </div>
    ));
  });

  return (
    <Card hoverable style={{marginBottom: 24}}>
      <h1>消息列表</h1>
      {msgList}
    </Card>
  )
}
```
这段代码直接在`useStore`中返回了整段jsx，是因为`map`的过程中会去访问数组的每一项来收集依赖，只有这样才能达到响应式的目的,或者在操作数组后，要将新数组重新赋值。


